/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.muni.fi.diplomka.servicelayer.gamelogic.managerimpl;

import cz.fi.muni.diplomka.common.Coordinates;
import cz.fi.muni.diplomka.common.enumeration.TileType;
import cz.muni.fi.diplomka.datalayer.dao.TileDao;
import cz.muni.fi.diplomka.datalayer.daoimpl.TileDaoImpl;
import cz.muni.fi.diplomka.datalayer.entity.TileEntity;
import cz.muni.fi.diplomka.servicelayer.dto.Tile;
import cz.muni.fi.diplomka.servicelayer.gamelogic.manager.WorldManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import org.dozer.DozerBeanMapper;
import org.dozer.Mapper;

/**
 *
 * @author Jakub Kutil
 */
public class WorldManagerImpl implements WorldManager {

    private TileDao tileDao = new TileDaoImpl();
    private Random random = new Random();
    private Mapper mapper = new DozerBeanMapper();

    @Override
    public List<Tile> getWorld() {
        List<Tile> tileList = new ArrayList<>();
        for (TileEntity tileEntity : tileDao.getAllTiles()) {
            tileList.add(mapper.map(tileEntity, Tile.class));
        }
        return tileList;
    }

    @Override
    public void generateWorld(int x, int y) {
        List<Tile> world = new ArrayList<>();

        if (x <= 0 || y <= 0) {
            throw new IllegalArgumentException("X and Y cant be less than zero");
        }

        if (x % 20 != 0 || y % 20 != 0) {
            throw new IllegalArgumentException("X and Y have to be multiples of 20");
        }


        for (int i = -y / 2; i <= y / 2; i++) {
            if (i != 0) {
                for (int j = -x / 2; j <= x / 2; j++) {
                    if (j != 0) {
                        Coordinates coordinates = new Coordinates();
                        coordinates.setX_coordinate(j);
                        coordinates.setY_coordinate(i);
                        Tile tile = new Tile(coordinates, TileType.plain);
                        world.add(tile);
                    }
                }
            }
        }
        for (int i = -y / 2; i <= y / 2; i++) {
            for (int j = -x / 2; j <= x / 2; j++) {
                if (i % 10 == 0 && j % 10 == 0 && i != 0 && j != 0) {
                    this.addTerrain(world, x / 2, y / 2, 3, TileType.mountain);
                    this.addTerrain(world, x / 2, y / 2, 2, TileType.village);
                    this.addTerrain(world, x / 2, y / 2, 1, TileType.fortress);
                }
            }
        }
        Collections.sort(world);
        for (Tile tile : world) {
            tileDao.addTile(mapper.map(tile, TileEntity.class));
        }
    }

    @Override
    public void addTerrain(List<Tile> world, int x, int y, int count, TileType type) {

        if (Math.abs(x) < 10 || Math.abs(y) < 10) {
            throw new IllegalArgumentException("x1 and y can not be between -9 to 9.");
        }

        if (count < 0) {
            throw new IllegalArgumentException("Count cant be lower than 0.");
        }

        int c = random.nextInt(3) + count;
        int xPos;
        int yPos;

        for (int i = 0; i <= 10; i++) {
            for (int j = 0; j <= 10; j++) {
                while (c > 0) {
                    if (x > 0) {
                        xPos = x - random.nextInt(10);
                    } else {
                        xPos = x + random.nextInt(10);
                    }
                    if (y > 0) {
                        yPos = y - random.nextInt(10);
                    } else {
                        yPos = y + random.nextInt(10);
                    }

                    Coordinates coordinates = new Coordinates();
                    coordinates.setX_coordinate(xPos);
                    coordinates.setY_coordinate(yPos);

                    Tile tile = new Tile();
                    tile.setCoordinates(coordinates);
                    if (world.get(world.indexOf(tile)).getType().toString().equals("plain")) {
                        world.get(world.indexOf(tile)).setType(type);
                        c--;
                    }
                }
            }
        }
    }

    @Override
    public void enlargeWorld() {
        List<Tile> world = new ArrayList<>();
        int x1 = this.getUpperLeftCorner().getX_coordinate();  // getting coordinates used for enlarging
        int y1 = this.getUpperLeftCorner().getY_coordinate();
        int x2 = this.getLowerRightCorner().getX_coordinate();
        int y2 = this.getLowerRightCorner().getY_coordinate();

        for (int i = x1 - 10; i <= x2 + 10; i++) {  // creating new tiles around current world
            for (int j = y1 + 10; j >= y2 - 10; j--) {
                if (i != 0 && j != 0 && ((i < x1 || i > x2) || (j > y1 || j < y2))) {

                    Coordinates coordinates = new Coordinates();
                    coordinates.setX_coordinate(i);
                    coordinates.setY_coordinate(j);
                    Tile tile = new Tile(coordinates, TileType.plain);
                    world.add(tile);
                }
            }
        }

        for (int i = x1 - 10; i <= x2 + 10; i++) {  // adding terrain to newly created tiles
            for (int j = y1 + 10; j >= y2 - 10; j--) {
                if (i % 10 == 0 && j % 10 == 0 && i != 0 && j != 0 && ((i < x1 || i > x2) || (j > y1 || j < y2))) {
                    this.addTerrain(world, i, j, 3, TileType.mountain);
                    this.addTerrain(world, i, j, 2, TileType.village);
                    this.addTerrain(world, i, j, 1, TileType.fortress);
                }

            }
        }
        for (Tile tile : world) {
            tileDao.addTile(mapper.map(tile, TileEntity.class));
        }
    }

    public void printWorld() {
        List<Tile> world = this.getWorld();
        Collections.sort(world);
        for (int i = this.getUpperLeftCorner().getY_coordinate(); i >= this.getLowerRightCorner().getY_coordinate(); i--) {
            for (int j = this.getUpperLeftCorner().getX_coordinate(); j <= this.getLowerRightCorner().getX_coordinate(); j++) {
                if (i != 0 && j != 0) {
                    Coordinates coordinates = new Coordinates();
                    coordinates.setX_coordinate(j);
                }
            }
        }
    }

    @Override
    public Coordinates getUpperLeftCorner() {
        return tileDao.getUpperLeftCorner();
    }

    @Override
    public Coordinates getLowerRightCorner() {
        return tileDao.getLowerRightCorner();
    }

    @Override
    public List<Tile> getSquareOfWorld(int x, int y) {
        if (x < this.getUpperLeftCorner().getX_coordinate()) {
            x = this.getUpperLeftCorner().getX_coordinate();
        }
        if (y > this.getUpperLeftCorner().getY_coordinate()) {
            y = this.getUpperLeftCorner().getY_coordinate();
        }
        if (x > this.getLowerRightCorner().getX_coordinate() - 10) {
            x = this.getLowerRightCorner().getX_coordinate() - 10;
        }
        if (y < this.getLowerRightCorner().getY_coordinate() + 10) {
            y = this.getLowerRightCorner().getY_coordinate() + 10;
        }
        List<Tile> tileList = new ArrayList<>();

        for (TileEntity tileEntity : tileDao.getSquareOfTiles(x, y)) {
            tileList.add(mapper.map(tileEntity, Tile.class));
        }
        Collections.sort(tileList);
        return tileList;
    }

    @Override
    public Tile getTileAtPosition(int x, int y) {
        return mapper.map(tileDao.getTileAtPosition(x, y), Tile.class);
    }

    @Override
    public void editTile(Tile tile) {
        if (tile == null) {
            throw new IllegalArgumentException("Tile cant be null.");
        }
        tileDao.editTile(mapper.map(tile, TileEntity.class));
    }
}
